#include <v8.h>

#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <iconv.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <vector>
#include <string>

#include <boost/scoped_array.hpp>
#include <boost/shared_array.hpp>
#include <boost/static_assert.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

#include "counterpartOpenDirectory.h"
#include "counterpartFormatString.h"

using namespace v8;
using namespace std;
using namespace boost;
using namespace boost::algorithm;

static const char kPWD [ ] = "PWD";
static const char kPNS [ ] = "/";

typedef Persistent <String>				PersistentString;
typedef Persistent <Object>				PersistentObject;
typedef Persistent <FunctionTemplate>	PersistentFunctionTemplate;

static const char * EnsureWorkingDirectoryEnvironmentVariable (void)
{
	const char * cwd = getenv (kPWD);
	if (cwd) return cwd;

	//	PWD was undefined (why?), so generate our return value the hard way.

	size_t bufferSize = 1024;

	while (true) // break in the middle
	{
		scoped_array <char> buffer (new char [bufferSize + 1]);
		cwd = getcwd (buffer.get ( ), bufferSize);

		if (cwd)
		{
			if (setenv (kPWD,cwd,true)) Throw (Exceptions::Errno ( ));
			cwd = getenv (kPWD);
			break;
		}

		if (ERANGE != errno) Throw (Exceptions::Errno ( ));
		bufferSize *= 2;
	}

	assert (cwd);
	return cwd;
}

static Handle <Value> getWorkingDirectory (const Arguments & arguments)
{
	try
	{
		if (arguments.Length ( )) 
		{
			DeclareSimpleException (NoArgumentsPlease);
			Throw (NoArgumentsPlease ( ));
		}

		Local <Array> result (Array::New ( ));
	
		const char * cwd (EnsureWorkingDirectoryEnvironmentVariable ( ));
		assert (cwd);	
	
		vector <string> splitWorkingDirectory;
	
		//	Note we DO let split tell us about the empty string preceding
		//	the first slash; the empty string signals that this is an
		//	absolute ("full") path.
	
		(void) split (splitWorkingDirectory, cwd, is_any_of (kPNS));
	
		size_t wdIndex (0);
	
		while (wdIndex < splitWorkingDirectory.size ( ))
		{
			const string & wdEntry (splitWorkingDirectory [wdIndex]);
	
			if (!result->Set (Integer::New (wdIndex), String::New (wdEntry.data ( ), wdEntry.size ( ))))
			{
				DeclareSimpleException (CouldNotAppendPathMember);
				Throw (CouldNotAppendPathMember ( ));
			}
	
			wdIndex += 1;
		}
	
		return result;
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static string MakeNativePath (Handle <Value> value)
{
	Demand (value->IsArray ( ));
	Local <Array> splitPath (Array::Cast (*value));
	size_t splitPathCount = splitPath->Length ( );
	Demand (0 != splitPathCount);		
	size_t splitPathIndex (0);
	Local <Integer> splitPathIndexHandle (Integer::New (splitPathIndex));
	Local <Value> pathElement (splitPath->Get (splitPathIndexHandle));
	Demand (pathElement->IsString ( ));
	String::Utf8Value firstPathElementUTF8 (pathElement);
	bool isAbsolute (!**firstPathElementUTF8);
	string pathString (isAbsolute ? "/" : EnsureWorkingDirectoryEnvironmentVariable ( ));
	splitPathIndex += isAbsolute;

	while (splitPathIndex < splitPathCount)
	{
		if (pathString.size ( ) > 1) pathString += kPNS;
		splitPathIndexHandle = Integer::New (splitPathIndex);
		pathElement = splitPath->Get (splitPathIndexHandle);
		Demand (pathElement->IsString ( ));
		String::Utf8Value pathElementUTF8 (pathElement);
		pathString += *pathElementUTF8;
		splitPathIndex += 1;
	}

	return pathString;
}

typedef shared_array <uint8_t> ByteBuffer;

static const size_t kReadBufferSize (1024);

struct OpenFile
{
		//	Numbers in JavaScript are floating-point, not integral, even
		//	though V8 provides integer accessors. Numbers are big enough
		//	to store values higher than 32-bit off_t but not big enough
		//	to store the highest 64-bit off_t value. We would like this
		//	code to be able cope with either kind of off_t, so it's
		//	best to defer range checking until runtime, which we do by
		//	verifying that we can convert the value we got into a double
		//	without approximation. What's odd about this is that due to
		//	the nature of floating point numbers we may report some very
		//	large file sizes which cannot be usefully incremented or
		//	decremented, but those would be for absurdly large files.

										OpenFile				(const Arguments &, PersistentObject &);
					void				Close					(void);
					Handle <Value>		Read					(size_t characterCount);
					ssize_t				Write					(const Local <Value> &);
					off_t				GetSize					(void);
					void				SetSize					(off_t);
					off_t				GetOffset				(void);
					void				SetOffset				(off_t);
		static		void				WeakReferenceCallback	(Persistent <Value> object, void * parameter);

	private :

										~OpenFile				(void); // must be instantiated on heap

					int					fFile;
					ByteBuffer			fReadBuffer;
					uint8_t *			fReadHead;
					iconv_t				fConversionDesc;
					size_t				fBufferedBytes;
					string				fFileEncoding,
										fStringEncoding;
					bool				fWantsRead		: 1,
										fWantsWrite		: 1;

		//	Although the system will be happy to reject attempts to write
		//	files not opened for writing and to read files not opened for
		//	reading, fWantsRead and fWantsWrite make it possible to throw
		//	more informative exceptions at the expense of a little more
		//	internal complexity.
};

OpenFile::~OpenFile (void)
{
	if (fFile != -1)
	{
		syslog (LOG_INFO, "OpenFile::~OpenFile closing file descriptor %i", fFile);
		if (close (fFile)) Throw (Exceptions::Errno ( ));
	}

	if (iconv_t (-1) != fConversionDesc)
	{
		if (iconv_close (fConversionDesc)) Throw (Exceptions::Errno ( ));
		fConversionDesc = iconv_t (-1);
	}
}

void OpenFile::WeakReferenceCallback (Persistent <Value> value, void * parameter)
{
	syslog (LOG_INFO, "OpenFile::WeakReferenceCallback");

	try
	{
		delete (OpenFile *) parameter;
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}
}

bool TestPropertyFlag (Object * object, Handle <String> propertyName, bool defaultValue)
{
	if (false == object->Has (propertyName)) return defaultValue;
	Local <Value> value (object->Get (propertyName));
	Demand (value->IsBoolean ( ));
	return value->IsTrue ( );
}

void OpenFile::Close (void)
{
	Demand (fFile != -1);
	syslog (LOG_INFO, "OpenFile::Close closing file descriptor %i", fFile);
	if (close (fFile)) Throw (Exceptions::Errno ( ));
	fFile = -1;
}

ssize_t OpenFile::Write (const Local <Value> & value)
{
	Demand (fWantsWrite);
	Demand (fFileEncoding == "UTF-8"); // for now

	String::Utf8Value utf8 (value);
	int toWrite = utf8.length ( );

	if (toWrite)
	{
		if (iconv_t (-1) != fConversionDesc)
		{
			size_t iconvResult (iconv (fConversionDesc, NULL, NULL, NULL, NULL));
			if (size_t (-1) == iconvResult) Throw (Exceptions::Errno ( ));
		}

		if (fBufferedBytes)
		{
			off_t result (lseek (fFile, -fBufferedBytes, SEEK_CUR));
			if (-1 == result) Throw (Exceptions::Errno ( ));
			fBufferedBytes = 0;
		}

		ssize_t written (write (fFile, *utf8, toWrite));
		if (-1 == written) Throw (Exceptions::Errno ( ));
		Demand (written == toWrite);
	}

	return toWrite;
}

static inline bool LittleEndian (void)
{
	static const uint16_t word (0x0001);
	return * (char *) &word;
}

Handle <Value> OpenFile::Read (size_t characterCount)
{
	Demand (fWantsRead);
	if (!characterCount) return String::New ("");

	scoped_array <uint16_t> resultBuffer (new uint16_t [characterCount]);
	uint16_t * resultCharacters (resultBuffer.get ( ));

	if (iconv_t (-1) == fConversionDesc)
	{
		fConversionDesc = iconv_open (fStringEncoding.c_str ( ), fFileEncoding.c_str ( ));
		if (iconv_t (-1) == fConversionDesc) Throw (Exceptions::Errno ( ));
	}

	if (!fReadBuffer)
	{
		fReadBuffer = ByteBuffer (new uint8_t [kReadBufferSize]);
		fBufferedBytes = 0;
	}

	size_t outBytesRemaining (characterCount * 2);
	assert (outBytesRemaining);

	do
	{
		if (!fBufferedBytes)
		{
			fReadHead = fReadBuffer.get ( );
			ssize_t bytesRead (read (fFile, fReadHead, kReadBufferSize));
			if (bytesRead < 0) Throw (Exceptions::Errno ( ));
			if (!bytesRead) break; // EOF
			assert (size_t (bytesRead) <= kReadBufferSize);
			fBufferedBytes = bytesRead;
		}

		size_t iconvResult (iconv (fConversionDesc, (char**) &fReadHead, &fBufferedBytes, (char**) &resultCharacters, &outBytesRemaining));

		if (size_t (-1) == iconvResult)
		{
			if (E2BIG == errno) break; // got what we came for
			Throw (Exceptions::Errno ( ));
		}

		if (iconvResult) Throw (Exceptions::ScopedMessage ("iconv", "could not convert reversibly"));
		assert (0 == outBytesRemaining % 2);
	}
	while (outBytesRemaining);

	return String::New (resultBuffer.get ( ), characterCount - (outBytesRemaining / 2));
}

static OpenFile * GetOpenFile (Object * o)
{
	assert (o);
	Local <Value> internalField (o->GetInternalField (0));
	Demand (internalField->IsExternal ( )); // cognitive dissonance
	External * external (External::Cast (*internalField));
	OpenFile * openFile ((OpenFile*) external->Value ( ));
	Demand (openFile);
	return openFile;
}

static Handle <Value> write (const Arguments & arguments)
{
	try
	{
		Demand (arguments.Length ( ) == 1);
		Object * writeObject (*(arguments.This ( )));
		OpenFile * openFile (GetOpenFile (writeObject));
		return Number::New (openFile->Write (arguments [0]));
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> read (const Arguments & arguments)
{
	try
	{
		Demand (arguments.Length ( ) == 1);
		Demand (arguments [0]->IsNumber ( ));
		double numberValue (arguments [0]->NumberValue ( ));
		Demand (size_t (numberValue) == numberValue);
		Object * readObject (*(arguments.This ( )));
		OpenFile * openFile (GetOpenFile (readObject));
		return openFile->Read (size_t (numberValue));
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> close (const Arguments & arguments)
{
	try
	{
		Demand (arguments.Length ( ) == 0);
		Object * closeObject (*(arguments.This ( )));
		OpenFile * openFile (GetOpenFile (closeObject));
		openFile->Close ( );
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

off_t OpenFile::GetSize (void)
{
	if (fFile == -1) return -1;
	struct stat statBuf;
	if (fstat (fFile, &statBuf)) Throw (Exceptions::Errno ( ));
	return statBuf.st_size;
}

static Handle <Value> getByteCount (Local <String> propertyName, const AccessorInfo & ai)
{
 	try
	{
		Local <Object> self = ai.Holder ( );
		OpenFile * openFile (GetOpenFile (*self));
		off_t gotSize (openFile->GetSize ( ));
		Demand (double (gotSize) == gotSize);
		return Number::New (gotSize);
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

void OpenFile::SetSize (off_t newSize)
{
	if (ftruncate (fFile, newSize)) Throw (Exceptions::Errno ( ));
}

static void setByteCount (Local <String> property, Local <Value> newValue, const AccessorInfo & ai)
{
 	try
	{
		Demand (newValue->IsNumber ( ));
		Local <Object> self = ai.Holder ( );
		OpenFile * openFile (GetOpenFile (*self));
		double newSize (newValue->NumberValue ( ));
		Demand (off_t (newSize) == newSize);
		openFile->SetSize (newSize);
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}
}

off_t OpenFile::GetOffset (void)
{
	if (fFile == -1) return -1;
	off_t result (lseek (fFile, 0, SEEK_CUR));
	if (-1 == result) Throw (Exceptions::Errno ( ));
	return result;
}

static Handle <Value> getByteOffset (Local <String> propertyName, const AccessorInfo & ai)
{
 	try
	{
		Local <Object> self = ai.Holder ( );
		OpenFile * openFile (GetOpenFile (*self));
		off_t gotOffset (openFile->GetOffset ( ));
		Demand (double (gotOffset) == gotOffset);
		return Number::New (gotOffset);
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

void OpenFile::SetOffset (off_t newOffset)
{
	// TODO: Can Windows set offset beyond EOF?
	off_t result (lseek (fFile, newOffset, SEEK_SET));
	if (-1 == result) Throw (Exceptions::Errno ( ));

	if (iconv_t (-1) != fConversionDesc)
	{
		size_t iconvResult (iconv (fConversionDesc, NULL, NULL, NULL, NULL));
		if (size_t (-1) == iconvResult) Throw (Exceptions::Errno ( ));
	}

	fBufferedBytes = 0;
}

static void setByteOffset (Local <String> property, Local <Value> newValue, const AccessorInfo & ai)
{
 	try
	{
		Demand (newValue->IsNumber ( ));
		Local <Object> self = ai.Holder ( );
		OpenFile * openFile (GetOpenFile (*self));
		double newOffset (newValue->NumberValue ( ));
		Demand (off_t (newOffset) == newOffset);
		openFile->SetOffset (newOffset);
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}
}

static Persistent <ObjectTemplate> & OpenFileObjectTemplate (void)
{
	static Persistent <ObjectTemplate> result;

	if (result.IsEmpty ( ))
	{
		result = Persistent <ObjectTemplate>::New (ObjectTemplate::New ( ));

		result->SetInternalFieldCount (1);

		result->SetAccessor (String::New ("byteCount"), getByteCount, setByteCount);
		result->SetAccessor (String::New ("byteOffset"), getByteOffset, setByteOffset);
	}

	return result;
}

OpenFile::OpenFile (const Arguments & arguments, PersistentObject & object) :
	fFile (-1), fConversionDesc (iconv_t (-1)), fBufferedBytes (0),
	fFileEncoding ("UTF-8"), fStringEncoding ("UCS-2"),
	fWantsRead (true), fWantsWrite (false)
{
	size_t argumentCount (arguments.Length ( ));
	Demand (1 == argumentCount || 2 == argumentCount);

	string nativePath (MakeNativePath (arguments [0]));
	Object * style (2 == argumentCount ? Object::Cast (*(arguments [1])) : NULL);
	int openFlags (O_NOCTTY | O_RDONLY);
	int openMode (S_IRUSR); // Windows can't turn this off.

	if (style)
	{
		static const PersistentString encodingPropertyName (PersistentString::New (String::New ("encoding")));

		if (style->Has (encodingPropertyName))
		{
			Local <Value> encodingPropertyValue (style->Get (encodingPropertyName));
			Demand (encodingPropertyValue->IsString ( ) || encodingPropertyValue->IsObject ( ));

			if (encodingPropertyValue->IsString ( )) // strings override default file encoding
			{
				fFileEncoding = *(String::Utf8Value (encodingPropertyValue));
			}
			else // objects potentially override both default encodings
			{
				Object * encodings = Object::Cast (*encodingPropertyValue);

				static const PersistentString fileEncodingPropertyName (PersistentString::New (String::New ("file")));

				if (encodings->Has (fileEncodingPropertyName))
				{
					fFileEncoding = * String::Utf8Value (encodings->Get (fileEncodingPropertyName));
					syslog (LOG_INFO, "opened file will have encoding %s", fFileEncoding.c_str ( ));
				}

				static const PersistentString stringEncodingPropertyName (PersistentString::New (String::New ("string")));

				if (encodings->Has (stringEncodingPropertyName))
				{
					fStringEncoding = * String::Utf8Value (encodings->Get (stringEncodingPropertyName));
					syslog (LOG_INFO, "strings from opened file will have encoding %s", fStringEncoding.c_str ( ));
					Demand (fStringEncoding == "UTF-16" || fStringEncoding == "UCS-2");

					if (fStringEncoding == "UTF-16")
					{
						fStringEncoding = LittleEndian ( ) ? "UTF-16LE" : "UTF-16BE";
					}
				}
			}
		}

		static const PersistentString readPropertyName (PersistentString::New (String::New ("read")));
		fWantsRead = TestPropertyFlag (style, readPropertyName, true);
		static const PersistentString writePropertyName (PersistentString::New (String::New ("write")));
		fWantsWrite = TestPropertyFlag (style, writePropertyName, false);
		static const PersistentString createProperty (PersistentString::New (String::New ("create")));
	
		if (style->Has (createProperty))
		{
			Local <Value> createValue (style->Get (createProperty));

			if (createValue->IsBoolean ( ))
			{
				if (createValue->IsTrue ( ))
				{
					openFlags |= O_CREAT;
					openMode |= S_IWUSR;
				}
			}
			else
			{
				openFlags |= O_CREAT;

				Demand (createValue->IsObject ( ));
				Object * createStyle (Object::Cast (*createValue));

				static const PersistentString mustCreateProperty (PersistentString::New (String::New ("must")));
				if (TestPropertyFlag (createStyle, mustCreateProperty, false)) openFlags |= O_EXCL;
			
				static const PersistentString readOnlyCreateProperty (PersistentString::New (String::New ("readOnly")));
				if (false == TestPropertyFlag (createStyle, readOnlyCreateProperty, false)) openMode |= S_IWUSR;
			}
		}
	}

	Demand (fWantsRead || fWantsWrite);

	if (fWantsRead && fWantsWrite)
	{
		openFlags &= ~O_RDONLY;
		openFlags |= O_RDWR;
	}
	else if (fWantsWrite)
	{
		openFlags &= ~O_RDONLY;
		openFlags |= O_WRONLY;
	}

	static PersistentFunctionTemplate templateRead (PersistentFunctionTemplate::New (FunctionTemplate::New (read)));
	object->Set (String::New ("read"), templateRead->GetFunction ( ));

	static PersistentFunctionTemplate templateWrite (PersistentFunctionTemplate::New (FunctionTemplate::New (write)));
	object->Set (String::New ("write"), templateWrite->GetFunction ( ));

	static PersistentFunctionTemplate templateClose (PersistentFunctionTemplate::New (FunctionTemplate::New (close)));
	object->Set (String::New ("close"), templateClose->GetFunction ( ));

	object->SetInternalField (0, External::New (this));

	syslog (LOG_INFO, "opening %s with openFlags 0x%08X and openMode 0x%08X", nativePath.c_str ( ), openFlags, openMode);
	fFile = open (nativePath.c_str ( ), openFlags, openMode);
	if (-1 == fFile) Throw (Exceptions::Errno ( ));
	syslog (LOG_INFO, "opened file descriptor %i", fFile);
}

static Handle <Value> openFile (const Arguments & arguments)
{
	try
	{
		PersistentObject object (PersistentObject::New (OpenFileObjectTemplate ( )->NewInstance ( )));
  		object.MakeWeak (new OpenFile (arguments,object), OpenFile::WeakReferenceCallback);
		return object;
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> remove (const Arguments & arguments)
{
	try
	{
		Demand (1 == arguments.Length ( ));
		string nativePath (MakeNativePath (arguments [0]));
		if (remove (nativePath.c_str ( ))) Throw (Exceptions::Errno ( ));
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> setWorkingDirectory (const Arguments & arguments)
{
	try
	{
		Demand (1 == arguments.Length ( ));
		string pathString (MakeNativePath (arguments [0]));
		syslog (LOG_ERR, "%s", pathString.c_str ( ));
		if (chdir (pathString.c_str ( ))) Throw (Exceptions::Errno ( ));
		if (setenv (kPWD, pathString.c_str ( ), true)) Throw (Exceptions::Errno ( ));
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Local <Value> enumerateDirectory (const string & nativePath, bool deep)
{
	OpenDirectory openDir (nativePath.c_str ( ));
	Local <Array> result (Array::New ( ));

	if (openDir)
	{
		while (struct dirent * dirEntry = openDir.NextEntry ( ))
		{
			if (strcmp (".", dirEntry->d_name) && strcmp ("..", dirEntry->d_name))
			{
				Local <Object> dirEntryObject (Object::New( ));

				const char * dirEntryType ("(unknown to extension)");

				// perhaps we should be pretending some of these don't exist for the sake of LCD Windows

				switch (dirEntry->d_type)
				{
					case DT_UNKNOWN		: dirEntryType = "(unknown to system)";		break;
					case DT_REG			: dirEntryType = "regular file";			break;
					case DT_DIR			: dirEntryType = "directory";				break;
					case DT_FIFO		: dirEntryType = "named pipe";				break; // makes no sense on Windows
					case DT_SOCK		: dirEntryType = "socket";					break; // makes no sense on Windows
					case DT_CHR			: dirEntryType = "character device";		break; // Windows doesn't make this distinction
					case DT_BLK			: dirEntryType = "block device";			break; // Windows doesn't make this distinction
					case DT_LNK			: dirEntryType = "symbolic link";			break; // Windows doesn't have
				}

				// should support timestamps; NTFS handles them all

				dirEntryObject->Set (String::New ("type"), String::New (dirEntryType));
				dirEntryObject->Set (String::New ("name"), String::New (dirEntry->d_name, strlen (dirEntry->d_name)));
				dirEntryObject->Set (String::New ("inode"), Number::New (dirEntry->d_ino));

				if (deep && DT_DIR == dirEntry->d_type)
				{
					dirEntryObject->Set (String::New ("children"), enumerateDirectory (nativePath + kPNS + dirEntry->d_name, deep));
				}

				if (!result->Set (Integer::New (result->Length ( )), dirEntryObject))
				{
					DeclareSimpleException (CouldNotAppendDirectoryEntry);
					Throw (CouldNotAppendDirectoryEntry ( ));
				}
			}
		}
	}

	return result;
}

static Handle <Value> enumerateDirectory (const Arguments & arguments)
{
	try
	{
		size_t argumentCount (arguments.Length ( ));
		Demand (1 == argumentCount || 2 == argumentCount);
		if (1 == argumentCount) return enumerateDirectory (MakeNativePath (arguments [0]), false);
		Demand (arguments [1]->IsBoolean ( ));
		return enumerateDirectory (MakeNativePath (arguments [0]), arguments [1]->BooleanValue ( ));
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

extern "C" Local <Value> instantiate (void)
{
	Local <Object> result (Object::New( ));

	Local <FunctionTemplate> templateGetWorkingDirectory (FunctionTemplate::New (getWorkingDirectory));
	result->Set (String::New ("getWorkingDirectory"), templateGetWorkingDirectory->GetFunction ( ));

	Local <FunctionTemplate> templateSetWorkingDirectory (FunctionTemplate::New (setWorkingDirectory));
	result->Set (String::New ("setWorkingDirectory"), templateSetWorkingDirectory->GetFunction ( ));

	Local <FunctionTemplate> templateEnumerateDirectory (FunctionTemplate::New (enumerateDirectory));
	result->Set (String::New ("enumerateDirectory"), templateEnumerateDirectory->GetFunction ( ));

	Local <FunctionTemplate> templateOpenFile (FunctionTemplate::New (openFile));
	result->Set (String::New ("openFile"), templateOpenFile->GetFunction ( ));

	Local <FunctionTemplate> templateRemove (FunctionTemplate::New (remove));
	result->Set (String::New ("remove"), templateRemove->GetFunction ( ));

	return result;
}
